本系列文章會在筆者的部落格繼續連載!Design System 101 感謝大家的閱讀!
HiHi! 中秋愉快!

在上面的篇章,我們提到了 style dictionary 的概念,以及如何運用它來管理視覺元素,而大家還記得先前提到從 figma 到 css 整個實踐的架構圖嗎?
沒錯!今天我們就要來實踐整個架構,而以下會是整個實踐的流程:
本篇將加入 design-tokens 這個 package,而從系列的第一篇文章至今,整體的 Design System 結構會是這樣:
design-system
├── README.md
├── package.json
├── pnpm-lock.yaml
├── packages
│   ├── components
|   |  ├── focus-scope
|   |  |── visually-hidden
│   └── tsconfig
|   |── design-tokens <-- new
...
首先在 packages 下新增一個 design-tokens 的資料夾,並且初始化 packages.json。
design-system > mkdir design-tokens
design-system > cd design-tokens
design-system > pnpm init
在 design-tokens 專案下安裝以下套件:
pnpm add style-dictionary sass postcss stylelint stylelint-config-prettier stylelint-config-standard stylelint-config-standard-scss stylelint-order stylelint-prettier -D
在 design-tokens 的根目錄底下建立 stylelintrc.json
design-system > touch stylelintrc.json
可以根據喜好設定 Stylelint,這是筆者的設定檔。
這次筆者會以 Material Design 的視覺元素與組件設計檔來作為 Design System 的基底。
Material Design 將 Design Token 切分成三個層級,分別為 Reference Token, System Token 以及最底層的 Component Token。
每一層的關係都在之前提過,可以參考 Design System 101 - Design Token,這裡就不在贅述。
由於 Material Design 將 Token 切分成三個層級,第一個問題就是要先定義哪一層級的 Token 需要納入 design-token 這個 packages 中管理?
在目前的架構中,筆者會將 Reference Token 與 System Token 納入 design-token,而 Component Token 則是放在各個組件中。
這也是現今許多 Design System 的做法,如 Pinterest 的 Gestalt 或是 Shopify 的 Polaris 等等。
接下來要將 Material Design 的視覺元素轉換成 key-value pair 的形式,這裡會以 color 作為範例,而以下是 design-tokens 的結構:
design-tokens
├── README.md
├── package.json
├── pnpm-lock.yaml
├── tokens
│   ├── color
│   |  ├── base.json <--- Reference Token
│   |  ├── alias.json <-- System Token
...
我們會將 Reference Token 與 System Token 分別存放在 base.json 與 alias.json,而在 alias.json 內的值則會參照 base.json。
這也呼應了前面所提到的一致性,當設計師或是當今天想要 rebrand,就只需要更改 Reference Token 的值,而不需要更改所有的 Design Token。

在 Material Design 中 (如上圖),可以看到所有 Reference Token 這是最上層也是最抽象的,而將其轉換成 Token 就會如下:
{
  "ref": {
    "palette": {
      "primary": {
        "0": { "value": "#000000" },
        "10": { "value": "#21005D" },
        ...
        "100": { "value": "#FFFFFF" }
      },
      "secondary": {
        "0": { "value": "#000000" },
        "10": { "value": "#1D192B" },
        ...
        "100": { "value": "#FFFFFF" }
      },
    }
  }
}

System Token 相較於 Reference Token 會有更明確的定義,例如 --sys-color-primary-container 可以清楚知道這是 primary 的顏色,且是用在 container 上。
大部分的 System Token 其背後都會有對應的 Reference Token,在 color 中便是如此:
{
  "sys": {
    "color": {
      "primary": {
        "light": { "value": "{ref.palette.primary.40.value}" },
      },
      "primary-container": {
        "light": { "value": "{ref.palette.primary.90.value}" },
      }
    }
    ...
  }
}
也可以定義該 System Token 在 dark mode 中的值
{
  "sys": {
    "color": {
      "primary": {
        "light": { "value": "{ref.palette.primary.40.value}" },
        "dark": { "value": "{ref.palette.primary.80.value}" }
      },
      "primary-container": {
        "light": { "value": "{ref.palette.primary.90.value}" },
        "dark": { "value": "{ref.palette.primary.30.value}" }
      }
    }
    ...
  }
}
當然視覺元素不會只有顏色,也會有字體、間距等等,其概念都是一樣的,這裡就不再一一贅述,可以參考筆者的 design-tokens。
最後在透過 style-dictionary 來產生不同平台的 Styling,而其會需要一個 config.js 來定義所有建置邏輯。
style-dictionary 還有另一個優點就是可以透過一些客製化的 script 來對其進行擴充,先來介紹最基本的用法。
{
  "source": ['./tokens/**/*.json'],
  "platforms": {
    "css": {
      "transformGroup": "css",
      "buildPath": "dist/css/",
      "files": [
        {
          "destination": "_variables.css",
          "format": "css/variables"
        }
      ]
    },
    "js": {
      "transformGroup": "js",
      "buildPath": "dist/js/",
      "files": [
        {
          "destination": "variables.js",
          "format": "javascript/es6"
        }
      ]
    }
  }
}
首先 source 是用來定義要轉換的檔案,像是上面的例子,我們會將所有的 token 都放在 tokens 底下,所以就可以設定 source 為 ./tokens/**/*.json
接下來就是 platforms,這裡會定義要轉換成哪些平台,像是 CSS、JS 等等,而每個平台都會有 transformGroup 與 files,可以參考 style-dictionary 的文檔。
而以下是筆者的設定
transformGroup 與 files。dist/normalize/normalize.css。這個好處是可以把一些基本的 CSS 與 Design Token 的 CSS 整理成一個檔案,而在引用的時候就可以直接引用 normalize.css。
最後,有了屬於自己 Design System 的 normalize.css 之後,就可以在專案中引入它,並且使用 Design Token 來進行開發了!
https://codesandbox.io/embed/cranky-meitner-nvynmh?fontsize=14&hidenavigation=1&theme=dark
可以嘗試將 light 改成 dark 來看看效果!
document.documentElement.setAttribute('data-theme', 'light');